using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using MediaOwl.Cryptos.Model;

namespace MediaOwl.Cryptos.Services
{
    [Export(typeof(RijndaelChiffre))]
    public class RijndaelChiffre
    {
        private readonly AesManaged aes;

        public RijndaelChiffre()
        {
            aes = new AesManaged();   
        }

        public IEnumerable<CryptValue<byte>> GetKey()
        {
            aes.GenerateKey();
            return aes.Key.Select(v => new CryptValue<byte>(v));
        }

        public IEnumerable<CryptValue<byte>> GetVector()
        {
            aes.GenerateIV();

            return aes.IV.Select(v => new CryptValue<byte>(v));
        }

        public string Encrypt(string plainText, byte[] key, byte[] initialisationVector)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (initialisationVector == null || initialisationVector.Length <= 0)
                throw new ArgumentNullException("key");

            // Declare the streams used
            // to encrypt to an in memory
            // array of bytes.
            MemoryStream msEncrypt = null;
            CryptoStream csEncrypt = null;
            StreamWriter swEncrypt = null;

            // Declare the AesManaged object
            // used to encrypt the data.

            // Declare the bytes used to hold the
            // encrypted data.

            try
            {
                // Create an AesManaged object
                // with the specified key and initialisationVector.
                var aesManagedAlg = new AesManaged
                                               {
                                                   Key = key,
                                                   IV = initialisationVector
                                               };

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = aesManagedAlg.CreateEncryptor(aesManagedAlg.Key, aesManagedAlg.IV);

                // Create the streams used for encryption.
                msEncrypt = new MemoryStream();
                csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
                swEncrypt = new StreamWriter(csEncrypt);

                //Write all data to the stream.
                swEncrypt.Write(plainText);

            }
            finally
            {
                // Clean things up.
                // Close the streams.
                if (swEncrypt != null)
                    swEncrypt.Close();
                if (csEncrypt != null)
                    csEncrypt.Close();
                if (msEncrypt != null)
                    msEncrypt.Close();
            }

            // Return the encrypted bytes from the memory stream.
            byte[] cipher = msEncrypt.ToArray();
            return cipher.Aggregate(string.Empty, (current, b) => current + (b + " "));
        }

        public string Decrypt(string cipherText, byte[] key, byte[] initialisationVector)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (initialisationVector == null || initialisationVector.Length <= 0)
                throw new ArgumentNullException("key");
            cipherText = cipherText.Trim();
            string[] cipherTextDivided = cipherText.Split(' ');
            var cipher = new byte[cipherTextDivided.Length];
            bool rightformat = true;
            for (int i = 0; i < cipherTextDivided.Length; i++)
            {
                if (rightformat)
                    rightformat = byte.TryParse(cipherTextDivided[i], out cipher[i]);
            }

            if (!rightformat)
            {
                return "Text can't be decrypted.\nCiphertext has inappropriate characters.";
            }

            // Declare the string used to hold
            // the decrypted text.
            string plaintext;

            try
            {
                // Create an AesManaged object
                // with the specified key and initialisationVector.
                var aesManagedAlg = new AesManaged
                                               {
                                                   Key = key,
                                                   IV = initialisationVector
                                               };

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesManagedAlg.CreateDecryptor(aesManagedAlg.Key, aesManagedAlg.IV);

                // Create the streams used for decryption.
                MemoryStream msDecrypt;
                using (msDecrypt = new MemoryStream(cipher))
                {
                    CryptoStream csDecrypt;
                    using (csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        StreamReader srDecrypt;
                        using (srDecrypt = new StreamReader(csDecrypt))
                        {
                            plaintext = srDecrypt.ReadToEnd();
                            srDecrypt.Close();
                        }
                        csDecrypt.Close();
                    }
                    msDecrypt.Close();
                }
            }
            catch
            {
                return "Text can't be decrypted.\nThe key or the vector seems to be wrong.";
                //return ex.Message;
            }

            return plaintext;

        }


    }
}